﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Win=System.Drawing;
using System.Linq;

namespace Jage.Logic.Sprites
{
    public class SpritesheetPngFactory
    {
        private const int MaxWidth = 2048; // Xna defined maximum Texture2D sizes
        private const int MaxHeight = 2048;

        static public SpritesheetPng Get(Data.Spritesheet spritesheetDef)
        {
            if (spritesheetDef.SpriteSources.Count > 0)
            {
                var spritesheet = new SpritesheetPng();
                int totalSizeX, totalSizeY;
                var spritesData = GetSpritesData(spritesheet, spritesheetDef, out totalSizeX, out totalSizeY);
                spritesheet.Sprites = GetSprites(spritesData);
                spritesheet.Bitmap = new Win.Bitmap(totalSizeX, totalSizeY);
                PlaceSprites(spritesheet, spritesData);
                return spritesheet;
            }
            else
            {
                throw new Exception("No sprites to bundle into a spritesheet.");
            }
        }

        private static void PlaceSprites(SpritesheetPng spritesheet, IEnumerable<SpritesheetPngSpriteData> sprites)
        {
            using (var graphics = Win.Graphics.FromImage(spritesheet.Bitmap))
            {
                foreach (var sprite in sprites)
                {
                    graphics.DrawImage(sprite.Bitmap, sprite.RectangleInSheet, sprite.Crop, Win.GraphicsUnit.Pixel);
                }
                graphics.Dispose();
            }
        }

        private static IEnumerable<SpritesheetPngSpriteData> GetSpritesData(SpritesheetPng parent, Data.Spritesheet spritesheetDef, out int sizeX, out int sizeY)
        {
            var sprites = new List<SpritesheetPngSpriteData>();

            var offset = new Point(0, 0);
            var maxHeightInRow = 0;
            sizeX = 0;
            sizeY = 0;

            foreach (var sprite in spritesheetDef.SpriteSources.OrderBy(s => s.SortIndex))
            {
                if (offset.X + sprite.CropWidth >= MaxWidth)
                {
                    offset = new Point(0, offset.Y + maxHeightInRow);
                    maxHeightInRow = 0;
                }
                sprites.Add(new SpritesheetPngSpriteData()
                                {
                                    Origin = new Win.Point(sprite.OriginX - sprite.CropX, sprite.OriginY - sprite.CropY),
                                    Spritesheet = parent,
                                    RectangleInSheet =
                                        new Win.Rectangle(offset.X, offset.Y, sprite.CropWidth, sprite.CropHeight),
                                    Bitmap = Resources.ResourceIO.GetBitmap(sprite.Resource),
                                    Crop =
                                        new Win.Rectangle(sprite.CropX, sprite.CropY, sprite.CropWidth,
                                                          sprite.CropHeight),
                                    SortIndex = sprite.SortIndex
                                });
                
                if (offset.X + sprite.CropWidth > sizeX) sizeX = offset.X + sprite.CropWidth;
                if (offset.Y + sprite.CropHeight > sizeY) sizeY = offset.Y + sprite.CropHeight;
                if (maxHeightInRow < sprite.CropHeight) maxHeightInRow = sprite.CropHeight;
                if (sizeY > MaxHeight) throw new Exception(string.Format("Spritesheet is bigger than allowed maximum of {0}x{1} pixels as of sprite \"{2}: {3}\"", MaxWidth, MaxHeight, sprite.SortIndex, sprite.Resource));

                offset = new Point(offset.X + sprite.CropWidth, offset.Y);
            }

            return sprites;
        }

        static private Sprite[] GetSprites(IEnumerable<SpritesheetPngSpriteData> spritesData)
        {
            return (from s in spritesData select new Sprite() { SortIndex = s.SortIndex, Origin = new Point(s.Origin.X, s.Origin.Y), RectangleInSheet = new Rectangle(s.RectangleInSheet.X, s.RectangleInSheet.Y, s.RectangleInSheet.Width, s.RectangleInSheet.Height)}).ToArray();
        }

    }
}
